/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * 
 * $Id: RangingData.hpp,v 1.20 2009-06-04 09:16:50 morsko Exp $
 *
 * \file
 * \brief MaCI - Ranging interface dataparser
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#ifndef _MACI_RANGINGDATA_HPP_
#define _MACI_RANGINGDATA_HPP_
#include "RangingTypes.hpp"
#include "MaCIData.hpp"
#include <stdio.h>
#include <vector>
#include "common/timestamp.hpp"

namespace MaCI {
  namespace Ranging {
    
    /** Real Data Encoder/Decoder for the Ranging Interface.
     *
     * This class contains the protocol encoder/decoder for the MaCI-Ranging interface
     */
    class CRangingData : public MaCI::CMaCIData
    {
    public:
      /** Constructor.
       *
       */
      CRangingData(void);


      /** Destructor.
       *
       */
      ~CRangingData();


      /** Copy constructor for Ranging
       */
      CRangingData(const CRangingData &);

      /** Assignment operator for Rangingdata.
       *
       * Use this with care; each call to this function copies data, 
       * so avoid it if possible.
       */
      CRangingData &operator=(const CRangingData &);

      /** Decode a BinBag container as RangingData container.
       * 
       * Implements the DecodeFunction defined in MaCI::CMaCIData.
       *
       * \see MaCI::CMaCIData
       * @param[in] aBinBag     Pointer to BinBag used as a backend.
       * @return                'true' -
       */
      bool DecodeFrom(gim::binbag::CBinBag *aBinBag = NULL);


      /** Resets the container.
       * 
       * This function resets the RangingData container. After reset,
       * the element is in the same state as after construction.
       */
      void Reset();
      

      /** Set Timestamp element.
       * 
       * \see Common::TTimestamp
       *
       * @param[in] aStamp      Timestamp element to set.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool SetTimestamp(const Common::TTimestamp &aStamp);


      /** Set DeviceInformationPosition element.
       * 
       * \see TDeviceInformationPosition
       *
       * @param[in] aPos        Position element to set.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool SetDeviceInformationPosition(const TDeviceInformationPosition &aPos);


      /** Set DeviceInformationLimits element.
       * 
       * \see TDeviceInformationLimits
       *
       * @param[in] aLim        Limits element to set.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool SetDeviceInformationLimits(const TDeviceInformationLimits &aLim);


      /** Set DistanceHeader element.
       *
       * \note This command must be followed by one or more calls to
       * AddDistance() for the RangingData container to become
       * valid. Number of 'AddDistance()' calls must match the number
       * of beams specified through this call.
       *
       * \see TDistanceHeader 
       *
       * @param[in] aHdr        DistanceHeaderTimestamp element to set.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool SetDistanceHeader(const TDistanceHeader &aHdr);


      /** Adds a Distance element. 
       *
       * This element has constraints about position in the BinBag
       * container. This element must come after TDistanceHeader or
       * another TDistance element. First TDistance must always come
       * after TDistanceHeader. Also, the number of 'AddDistance' calls
       * must match the element count provided in the
       * 'TDistanceHeader'.
       * 
       * \see SetDistanceHeader()
       * \see TDistanceHeader
       * \see TDistance
       * 
       * @param[in] aDist       Distance element to add.
       * @return                'true' if the specified constraints are met.
       *                        'false' if constrains are not met, 
       *                        or no valid binbag::CBinBag backend exists.
       */
      bool AddDistance(const TDistance &aDist);

      /** Adds a Reflection element. 
       *
       * This element has constraints about position in the BinBag
       * container. This element must come after TDistanceHeader,
       * TDistance or another TReflection element. If addid TReflection values, it must
       * be defined at TDistanceHeader. Also, the number of 'AddReflection' calls
       * must match the element count provided in the
       * 'TDistanceHeader'.
       * 
       * \see SetDistanceHeader()
       * \see TDistanceHeader
       * \see TReflection
       * 
       * @param[in] aRefl       Reflection element to add.
       * @return                'true' if the specified constraints are met.
       *                        'false' if constrains are not met, 
       *                        or no valid binbag::CBinBag backend exists.
       */
      bool AddReflection(const TReflection &aRefl);

      /** Add command element.
       * 
       * This element has constrains about position in the BinBag
       * container. First element can be anywhere, but all other
       * commands must be in sequence. The container cannot contain
       * multiple sequences of commands.
       *
       * \note In this interface there can be multiple commands in one 
       * send (This is mostly for testing purposes, so this feature
       * might disappear in the future)
       *
       * \see TCommand
       * 
       * @param[in] aCmd        Command element to add.
       * @return                'true' if no such element previously
       *                        exists, and a valid binbag::CBinBag backend exists.
       */
      bool AddCommand(const TCommand &aCmd);
      

      /** Get pointer to Common::TTimestamp element.
       *
       * \see Common::TTimestamp
       * 
       * @return                Constant pointer to timestamp
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const Common::TTimestamp *GetTimestamp(void) const;



      /** Get pointer to TDeviceInformationPosition element.
       *
       * \see TDeviceInformationPosition
       * 
       * @return                Constant pointer to position
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TDeviceInformationPosition *GetDeviceInformationPosition(void) const;


      /** Get pointer to TDeviceInformationLimits element.
       * 
       * \see TDeviceInformationLimits
       * 
       * @return                Constant pointer to limits
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TDeviceInformationLimits *GetDeviceInformationLimits(void) const;


      /** Get pointer to TDistanceHeader element.
       * 
       * \see TDistanceHeader
       *
       * @return                Constant pointer to distanceheader
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TDistanceHeader *GetDistanceHeader(void) const;


      /** Get pointer to TDistance element by index.
       * 
       * This function returns a pointer to distance element by index.
       * To read through all the distances in the container, call this
       * function while incrementing index from 0 up. When the
       * function return NULL, there is no more elements available.
       *
       * \see TDistance
       * \see GetDistanceElementCount()
       *
       * @param[in] aIndex      Index of TDistance element to get
       *                        pointer to. First element is 0.
       *                        If invalid/non-existing index
       *                        is specified, NULL is returned.
       * @return                Constant pointer to distance
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TDistance *GetDistance(const int aIndex) const;

      /** Get pointer to TReflection element by index.
       * 
       * This function returns a pointer to reflection element by index.
       * To read through all the distances in the container, call this
       * function while incrementing index from 0 up. When the
       * function return NULL, there is no more elements available.
       *
       * \see TReflection
       * \see GetDistanceElementCount()
       *
       * @param[in] aIndex      Index of TReflection element to get
       *                        pointer to. First element is 0.
       *                        If invalid/non-existing index
       *                        is specified, NULL is returned.
       * @return                Constant pointer to distance
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TReflection *GetReflection(const int aIndex) const;

      /** Get number of Distance elements in this container.
       *
       * @return                Number of TDistance elements in
       *                        container. If this container
       *                        doesn't contain Distances, zero
       *                        is returned.
       */
      int GetDistanceElementCount(void) const;

      /** Get number of Reflection elements in this container.
       *
       * @return                Number of TReflection elements in
       *                        container. If this container
       *                        doesn't contain Distances, zero
       *                        is returned.
       */
      int GetReflectionElementCount(void) const;

      /** Get pointer to Command by index.
       * 
       * This function returns a pointer to command element by index.
       * To read through all the commands in the container, call this
       * function while incrementing index from 0 up. When the
       * function return NULL, there is no more elements available.
       *
       * \see SetCommand()
       * \see TCommand
       *
       * @param[in] aIndex      Number of command to get pointer to.
       *                        If this parameter is omitted, default
       *                        value of zero is used.
       * @return                Constant pointer to command
       *                        element if such element exists
       *                        in the container. If the element 
       *                        doesn't exist, NULL is returned.
       */
      const TCommand *GetCommand(const int aIndex = 0) const;


      /** Get number of Command elements in this container.
       *
       * @return                Number of TCommand elements in
       *                        container. If this container
       *                        doesn't contain Commands, zero
       *                        is returned.
       */
      int GetCommandCount(void) const;
      
      /** Returns whether the container is in valid state. (Integrity checks OK)
       * @return                'true' if valid, 'false' if invalid.
       */
      inline bool IsValid(void) const {
        return iValid;
      }
      
      /** Dump RangingData container contents through dPrint facility.
       *
       * @param[in] level       Debug level to use for printout.
       */
      void Print(const int level) const;

    protected:
    private:
      

      bool iValid;

      const Common::TTimestamp *iTimestampPtr; ///< Pointer to timestamp if exists
      const TDeviceInformationPosition *iPositionPtr; ///<  Pointer to position if exists
      const TDeviceInformationLimits *iLimitsPtr; ///< Ptr to limits if exists
      const TDistanceHeader *iDistanceHeaderPtr; ///< pointer to distancehdr if exists
      const TDistance *iFirstDistancePtr; ///< pointer to first distance if exists
      const TReflection *iFirstReflectionPtr; ///< pointer to first reflection if exists
      int iDistanceElementCount; ///< For analysing packet correctness
      int iHeaderDistanceElementCount; ///< Number of elements set in header.
      int iReflectionElementCount; ///< For analysing packet correctness
      int iHeaderReflectionElementCount; ///< Number of elements set in header.
      const TCommand *iFirstCommandPtr; ///< Pointer to First Command if any.
      int iCommandCount; ///< Number of commands in container
    };
  }
}
#endif
